#ifndef _SPI_HW_H_
#define _SPI_HW_H_

#include "soc.h"

/**
 * SPI模块支持SPI/I2S/SPI_TI三种模式
 */

typedef struct {
	_RW CR1;        /*!< SPI control register 1 (not used in I2S mode),      offset: 0x00 */
	_RW CR2;        /*!< SPI control register 2,                             offset: 0x04 */
	_RW SR;         /*!< SPI status register,                                offset: 0x08 */
	_RW DR;         /*!< SPI data register,                                  offset: 0x0C */
	_RW CRCPR;      /*!< SPI CRC polynomial register (not used in I2S mode), offset: 0x10 */
	_OR RXCRCR;     /*!< SPI RX CRC register (not used in I2S mode),         offset: 0x14 */
	_OR TXCRCR;     /*!< SPI TX CRC register (not used in I2S mode),         offset: 0x18 */
	_RW I2SCFGR;    /*!< SPI_I2S configuration register,                     offset: 0x1C */
	_RW I2SPR;      /*!< SPI_I2S prescaler register,                         offset: 0x20 */
} SPI_Struct;

extern SPI_Struct SOC_SPI1;       // 最高42MHz
extern SPI_Struct SOC_SPI2_I2S2;  // 最高21MHz
extern SPI_Struct SOC_SPI3_I2S3;  // 最高21MHz
extern SPI_Struct SOC_SPI4;
extern SPI_Struct SOC_SPI5;
extern SPI_Struct SOC_SPI6;

/**
 * SPI控制
 */
#define SPI_CR1_CPHA_FIRST              (0x0U<<0)  // 时钟相位，从第一个时钟边沿采样数据，通信时不能修改此位，不适用于I2S和TI模式
#define SPI_CR1_CPHA_SECOND             (0x1U<<0)
#define SPI_CR1_CPOL_LOW                (0x0U<<1)  // 空闲电平为低，不适用于I2S和TI模式
#define SPI_CR1_CPOL_HIGH               (0x1U<<1)
#define SPI_CR1_MSTR_SLAVE              (0x0U<<2)
#define SPI_CR1_MSTR_MASTER             (0x1U<<2)  // 主机模式，不适用于I2S
#define SPI_CR1_BR_DIV2                 (0x0U<<3)  // 时钟2分频，不适用于I2S和SLAVE模式
#define SPI_CR1_BR_DIV4                 (0x1U<<3)
#define SPI_CR1_BR_DIV8                 (0x2U<<3)
#define SPI_CR1_BR_DIV16                (0x3U<<3)
#define SPI_CR1_BR_DIV32                (0x4U<<3)
#define SPI_CR1_BR_DIV64                (0x5U<<3)
#define SPI_CR1_BR_DIV128               (0x6U<<3)
#define SPI_CR1_BR_DIV256               (0x7U<<3)
#define SPI_CR1_SPE_DIS                 (0x0U<<6)  // 关闭SPI前应先确保数据已发送或接收完成
#define SPI_CR1_SPE_EN                  (0x1U<<6)  // SPI使能，不适用于I2S
#define SPI_CR1_LSBFIRST_DIS            (0x0U<<7)  // 先发送高bit，不适用于I2S和TI模式
#define SPI_CR1_LSBFIRST_EN             (0x1U<<7)
#define SPI_CR1_SS_HARD                 (0x0U<<8)  // 由硬件控制CS，SPI的NSS连接到对应的GPIO上，不适用于I2S和TI模式
#define SPI_CR1_SS_LOW                  (0x2U<<8)  // 由软件控制CS，SPI的NSS固定为低，允许动态修改，Slave下，需要在此模式下才能正常收发
#define SPI_CR1_SS_HIGH                 (0x3U<<8)  // 由软件控制CS，SPI的NSS固定为高，Master下软件CS必须选这个，否者会设置MODF_EN
#define SPI_CR1_RXONLY_DIS              (0x0U<<10) // 全双工，该位段在BIDIMODE_SINGLE模式下有效，写DR的时候开始数据传输
#define SPI_CR1_RXONLY_EN               (0x1U<<10) // 关闭输出，仅接收，不适用于I2S，SPE_EN的时候开始数据传输
#define SPI_CR1_DFF_8B                  (0x0U<<11) // 8bits数据帧
#define SPI_CR1_DFF_16B                 (0x1U<<11) // 16bits数据帧，适用于I2S
#define SPI_CR1_NEXT_DTAT               (0x0U<<12) // 数据阶段
#define SPI_CR1_NEXT_CRC                (0x1U<<12) // 下一个传输为CRC，不适用于I2S，当 SPI 配置为全双工或只发送模式时，只要最后一个数据写入 SPI_DR 寄存器，就必须对 CRCNEXT 执行写操作。当 SPI 配置为只接收模式时，必须在接收到倒数第二个数据之后将 CRCNEXT 置 1。当传输由 DMA 管理时，此位应保持清零状态
#define SPI_CR1_CRCEN_DIS               (0x0U<<13)
#define SPI_CR1_CRCEN_EN                (0x1U<<13) // 使能CRC计算
#define SPI_CR1_BIDIOE_RX               (0x0U<<14) // 仅接收，该位段在BIDIMODE_DOUBLE模式下有效，不适用于I2S，SPE_EN的时候开始数据传输
#define SPI_CR1_BIDIOE_TX               (0x1U<<14) // 仅发送，写DR的时候开始数据传输
#define SPI_CR1_BIDIMODE_NORMAL         (0x0U<<15) // 普通SPI，通过RXONLY来控制是否使能发送，不适用于I2S
#define SPI_CR1_BIDIMODE_SINGLE         (0x1U<<15) // 单线SPI，主机MOSI/从机MISO用作双向数据线，数据方向取决于BIDIOE

/**
 * 中断使能
 */
#define SPI_CR2_RXDMAEN_DIS             (0x0U<<0)
#define SPI_CR2_RXDMAEN_EN              (0x1U<<0)  // 使能RX DMA
#define SPI_CR2_TXDMAEN_DIS             (0x0U<<1)
#define SPI_CR2_TXDMAEN_EN              (0x1U<<1)  // 使能TX DMA
#define SPI_CR2_SSOE_DIS                (0x0U<<2)  // 在主模式下禁止 SS 输出，可在多主模式配置下工作，此时NSS应配置成输入模式，这样便可以被选中
#define SPI_CR2_SSOE_EN                 (0x1U<<2)  // 在主模式下使能 SS 输出，不能在多主模式环境下工作
#define SPI_CR2_FRF_SPI                 (0x0U<<4)  // 普通SPI，不适用于I2S
#define SPI_CR2_FRF_TI                  (0x1U<<4)  // TI的兼容模式
#define SPI_CR2_ERRIE_DIS               (0x0U<<5)
#define SPI_CR2_ERRIE_EN                (0x1U<<5)  // 使能错误中断，包括CRCERR、OVR、MODF、UDR、OVR、FRE
#define SPI_CR2_RXNEIE_DIS              (0x0U<<6)
#define SPI_CR2_RXNEIE_EN               (0x1U<<6)  // 使能接收数据可读中断
#define SPI_CR2_TXEIE_DIS               (0x0U<<7)
#define SPI_CR2_TXEIE_EN                (0x1U<<7)  // 使能发送为空中断

/**
 * 状态
 */
#define SPI_SR_RXNE_EN                  (0x1U<<0)  // 接收非空
#define SPI_SR_TXE_EN                   (0x1U<<1)  // 发送为空
#define SPI_SR_CHSIDE_LEFT              (0x0U<<2)  // 发送或接收左通道信息，I2S模式，在PCM模式下无意义
#define SPI_SR_CHSIDE_RIGHT             (0x1U<<2)  // 发送或接收右通道信息
#define SPI_SR_CHSIDE_MASK              (0x1U<<2)
#define SPI_SR_UDR_EN                   (0x1U<<3)  // 发生下溢，不适用于SPI，读清
#define SPI_SR_CRCERR_EN                (0x1U<<4)  // CRC校验失败，写0清，不适用I2S
#define SPI_SR_MODF_EN                  (0x1U<<5)  // 发生故障，不适用I2S，读或写SR再写CR1可清除
#define SPI_SR_OVR_EN                   (0x1U<<6)  // 发生上溢，对DR进行读操作，再读SR可清除
#define SPI_SR_BSY_EN                   (0x1U<<7)  // SPI/I2S正忙，硬件置位和清除，这个标志有一些使用限制，请查看手册
#define SPI_SR_FRE_EN                   (0x1U<<8)  // 帧格式错误，TI从/I2S从模式下有效，读SR清除

/**
 * 数据，8bits帧时，仅使用[7:0]，[15:8]保持为0
 */
#define SPI_DR_

/**
 * CRC，包含用于 CRC 计算的多项式，CRC 多项式 (0007h) 是此寄存器的复位值。
 * 可根据需要配置另一个多项式。不适用于I2S
 */
#define SPI_CRCCPR_

/**
 * CRC接收寄存器，只读，CRCEN写1复位，不适用于I2S
 */
#define SPI_RXCRCR_

/**
 * CRC发送寄存器，只读，CRCEN写1复位，不适用于I2S
 */
#define SPI_TXCRCR_

/**
 * I2S配置
 */
#define SPI_I2SCFGR_CHLEN_16B           (0x0U<<0)  // 每个音频通道的位数16bits，只有DATLEN_16B才有效
#define SPI_I2SCFGR_CHLEN_32B           (0x1U<<0)  // DATLEN为16或32时，硬件强制为该值，不适用SPI
#define SPI_I2SCFGR_DATLEN_16B          (0x0U<<1)  // 传输数据长度16bits，不适用SPI
#define SPI_I2SCFGR_DATLEN_24B          (0x1U<<1)
#define SPI_I2SCFGR_DATLEN_32B          (0x2U<<1)
#define SPI_I2SCFGR_CKPOL_LOW           (0x0U<<3)  // 空闲电平为低，不适用SPI
#define SPI_I2SCFGR_CKPOL_HIGH          (0x1U<<3)
#define SPI_I2SCFGR_I2SSTD_PHILIPS      (0x0U<<4)  // PHILIPS标准，不适用SPI
#define SPI_I2SCFGR_I2SSTD_MSB          (0x1U<<4)  // 对齐标准，左对齐
#define SPI_I2SCFGR_I2SSTD_LSB          (0x2U<<4)  // 对齐标准，右对齐
#define SPI_I2SCFGR_I2SSTD_PCM          (0x3U<<4)  // PCM标准
#define SPI_I2SCFGR_PCMSYNC_SHORT       (0x0U<<7)  // PCM短帧同步，不适用SPI，仅PCM标准下有效
#define SPI_I2SCFGR_PCMSYNC_LONG        (0x1U<<7)  // PCM长帧同步
#define SPI_I2SCFGR_I2SCFG_SLAVE_TX     (0x0U<<8)  // 从模式发送，不适用SPI
#define SPI_I2SCFGR_I2SCFG_SLAVE_RX     (0x1U<<8)
#define SPI_I2SCFGR_I2SCFG_MASTER_TX    (0x2U<<8)  // 主模式发送，不适用SPI
#define SPI_I2SCFGR_I2SCFG_MASTER_RX    (0x3U<<8)
#define SPI_I2SCFGR_I2SE_DIS            (0x0U<<10)
#define SPI_I2SCFGR_I2SE_EN             (0x1U<<10) // 使能I2S
#define SPI_I2SCFGR_I2SMOD_SPI          (0x0U<<11) // 使能SPI模式
#define SPI_I2SCFGR_I2SMOD_I2S          (0x1U<<11) // 使能I2S模式

/**
 * I2S预分频
 */
#define SPI_I2SPR_I2SDIV_V_4_511(val)   (((val)&0x1?0x100:0x0)|((val)/2))  // 仅I2S主模式才会使用该值
#define SPI_I2SPR_MCKOE_DIS             (0x0U<<9)
#define SPI_I2SPR_MCKOE_EN              (0x1U<<9)  // 使能主时钟输出，仅在I2S主模式下有效

#endif /* _SPI_HW_H_ */
